﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace ThreadSafeControls {
    public class ThreadSafeTreeNodeCollection : IList<ThreadSafeTreeNode>, IList {

        private readonly TreeNodeCollection _nodes;
        private readonly ThreadSafeTreeView _treeView;

        internal ThreadSafeTreeNodeCollection(TreeNodeCollection nodes, ThreadSafeTreeView treeView) {
            if (nodes == null)
                throw new ArgumentNullException("nodes");
            else if (treeView == null)
                throw new ArgumentNullException("treeView");

            _nodes = nodes;
            _treeView = treeView;
        }

        public IList<ThreadSafeTreeNode> Synchronize() {
            return new SynchronizedObjectCollection<ThreadSafeTreeNode, ThreadSafeTreeNodeCollection>(this);
        }

        public ThreadSafeTreeNode this[int index] {
            get { return _treeView.ReturnAsync(c => _nodes[index]).AsThreadSafe(_treeView); }
            set { _treeView.UpdateAsync(c => _nodes[index] = value.TreeNode); }
        }

        public ThreadSafeTreeNode this[string key] {
            get { return _treeView.ReturnAsync(c => _nodes[key]).AsThreadSafe(_treeView); }
        }

        public int Count {
            get { return _treeView.ReturnAsync(c => _nodes.Count); }
        }

        public bool IsReadOnly {
            get { return _nodes.IsReadOnly; }
        }

        public ThreadSafeTreeNode Add(string text) {
            return _treeView.ReturnAsync(c => _nodes.Add(text)).AsThreadSafe(_treeView);
        }

        public int Add(TreeNode node) {
            return _treeView.ReturnAsync(c => _nodes.Add(node));
        }

        public int Add(ThreadSafeTreeNode node) {
            return Add(node.TreeNode);
        }

        public ThreadSafeTreeNode Add(string key, string text) {
            return _treeView.ReturnAsync(c => _nodes.Add(key, text)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Add(string key, string text, int imageIndex) {
            return _treeView.ReturnAsync(c => _nodes.Add(key, text, imageIndex)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Add(string key, string text, string imageKey) {
            return _treeView.ReturnAsync(c => _nodes.Add(key, text, imageKey)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Add(string key, string text, int imageIndex, int selectedImageIndex) {
            return _treeView.ReturnAsync(c => _nodes.Add(key, text, imageIndex, selectedImageIndex)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Add(string key, string text, string imageKey, string selectedImageKey) {
            return _treeView.ReturnAsync(c => _nodes.Add(key, text, imageKey, selectedImageKey)).AsThreadSafe(_treeView);
        }

        public void Clear() {
            _treeView.UpdateAsync(c => _nodes.Clear());
        }

        public bool Contains(TreeNode node) {
            return _treeView.ReturnAsync(c => _nodes.Contains(node));
        }

        public bool Contains(ThreadSafeTreeNode node) {
            return Contains(node.TreeNode);
        }

        public bool ContainsKey(string key) {
            return _treeView.ReturnAsync(c => _nodes.ContainsKey(key));
        }

        public void CopyTo(ThreadSafeTreeNode[] dest, int index) {
            var array = Enumerate().ToArray();
            array.CopyTo(dest, index);
        }

        public ThreadSafeTreeNode[] Find(string key, bool searchAllChildren) {
            return _treeView.ReturnAsync(c => _nodes.Find(key, searchAllChildren).Select(n => n.AsThreadSafe(_treeView)).ToArray());
        }

        public int IndexOf(TreeNode node) {
            return _treeView.ReturnAsync(c => _nodes.IndexOf(node));
        }

        public int IndexOf(ThreadSafeTreeNode node) {
            return IndexOf(node.TreeNode);
        }

        public int IndexOfKey(string key) {
            return _treeView.ReturnAsync(c => _nodes.IndexOfKey(key));
        }

        public ThreadSafeTreeNode Insert(int index, string text) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, text)).AsThreadSafe(_treeView);
        }

        public void Insert(int index, TreeNode node) {
            _treeView.UpdateAsync(c => _nodes.Insert(index, node));
        }

        public void Insert(int index, ThreadSafeTreeNode node) {
            Insert(index, node.TreeNode);
        }

        public ThreadSafeTreeNode Insert(int index, string key, string text) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, key, text)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Insert(int index, string key, string text, int imageIndex) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, key, text, imageIndex)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Insert(int index, string key, string text, string imageKey) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, key, text, imageKey)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Insert(int index, string key, string text, int imageIndex, int selectedImageIndex) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, key, text, imageIndex, selectedImageIndex)).AsThreadSafe(_treeView);
        }

        public ThreadSafeTreeNode Insert(int index, string key, string text, string imageKey, string selectedImageKey) {
            return _treeView.ReturnAsync(c => _nodes.Insert(index, key, text, imageKey, selectedImageKey)).AsThreadSafe(_treeView);
        }

        public void Remove(TreeNode node) {
            _treeView.UpdateAsync(c => _nodes.Remove(node));
        }

        public bool Remove(ThreadSafeTreeNode node) {
            Remove(node.TreeNode);
            return true;
        }

        public void RemoveAt(int index) {
            _treeView.UpdateAsync(c => _nodes.RemoveAt(index));
        }

        public void RemoveByKey(string key) {
            _treeView.UpdateAsync(c => _nodes.RemoveByKey(key));
        }

        public IEnumerator<ThreadSafeTreeNode> GetEnumerator() {
            return Enumerate().GetEnumerator();
        }

        private IEnumerable<ThreadSafeTreeNode> Enumerate() {
            return _treeView.ReturnAsync(c => _nodes)
                .Cast<TreeNode>()
                .Select(n => n.AsThreadSafe(_treeView));
        }

        #region Explicit Interface Members

        object IList.this[int index] {
            get {
                return _treeView.ReturnAsync(c => ((IList)_nodes)[index]);
            }
            set {
                _treeView.UpdateAsync(c => ((IList)_nodes)[index] = value);
            }
        }

        bool IList.IsFixedSize {
            get { return ((IList)_nodes).IsFixedSize; }
        }

        bool ICollection.IsSynchronized {
            get { return ((ICollection)_nodes).IsSynchronized; }
        }

        object ICollection.SyncRoot {
            get { return ((ICollection)_nodes).SyncRoot; }
        }

        void ICollection<ThreadSafeTreeNode>.Add(ThreadSafeTreeNode node) {
            Add(node);
        }

        int IList.Add(object value) {
            return _treeView.ReturnAsync(c => ((IList)_nodes).Add(value));
        }

        bool IList.Contains(object value) {
            return _treeView.ReturnAsync(c => ((IList)_nodes).Contains(value));
        }

        void ICollection.CopyTo(Array dest, int index) {
            var array = Enumerate().ToArray();
            array.CopyTo(dest, index);
        }

        int IList.IndexOf(object value) {
            return _treeView.ReturnAsync(c => ((IList)_nodes).IndexOf(value));
        }

        void IList.Insert(int index, object value) {
            _treeView.UpdateAsync(c => ((IList)_nodes).Insert(index, value));
        }

        void IList.Remove(object value) {
            _treeView.UpdateAsync(c => ((IList)_nodes).Remove(value));
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        #endregion
    }
}
